/*
    This is a set of libraries to assist in OpenGl and Other Applications
    Copyright (C) 2008  The Modular Engine Team

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
#ifndef TESTMODULE_H
#define TESTMODULE_H
#include <iostream>
#include "math.h"
#include "EngineCore.h"
#include "ConsoleModule.h"
#include "OpenglModule.h"
#include "HUDButton.h"
#include "HUDModule.h"
#include "HUDTextbox.h"
#include "TimerModule.h"
#include "HUDSlider.h"
#include "HUDCheckbox.h"
#include "HUDScrollbar.h"
#include "HUDWindow.h"
#include "HUDLabel.h"
#include "consoleFunctions.h"
#include "ServerModule.h"
#include "ClientModule.h"
#include "RegistryModule.h"
#include "GraphicsModule.h"
#include "InputModule.h"
#include "InterfaceModule.h" 
#include "InterfaceContainer.h" 

#include <string>

#pragma comment( lib, "WS2_32.lib" )


#define TB(tb, pos, dims) \
	tb = new HUDTextbox(); \
	tb->mvPosition = pos; \
	tb->mvDimensions = dims;

#define BTN( btn, pos, dims, text ) \
	btn = new HUDButton(); \
	btn->mvPosition = pos; \
	btn->mvDimensions = dims; \
	btn->buttonText( text );

#define WND( wnd, pos, dims, title )  \
	wnd = new HUDWindow(); \
	wnd->mvPosition = pos; \
	wnd->mvDimensions = dims; \
	wnd->setWindowTitle( title );

#define ADDELEMENT( element ) \
	ModularEngine::HUDModule::getSingleton().addElement( element );

#define DEBUG_MD

namespace ModularEngine
{

	CFUNCTOR( cCLEAR, "clear" );

	class TestModule: BASE(TestModule)
	{
	public:

		Timer mTimer;
		char *fps;

		// The Windows
		HUDWindow *mConsoleWindow;
		HUDWindow *mMainMenu;

		// Elements for console
		HUDTextbox *userCommand;
		HUDTextbox *consoleText;
		HUDButton *sendConsole;

		// Elements for main menu
		HUDButton *hostServer;
		HUDButton *connectToServer;
		HUDButton *disconnect;
		HUDButton *showSettings;
		HUDButton *exit;

		BitmapFont bFont;
		BitmapFont bFont2;

		vector<string> *foxLines;
	public:

		~TestModule();

		class WhiteBoard
		{
		public:
			vector<Vector3d> mvPoints;
			vector<vector<Vector3d>> wbSplines;
			bool bReset;
			Timer mMouseTimer;
			void capturePoints();
			void drawWhiteBoard();
			void start()
			{
				mMouseTimer.reset();
				bReset = true;
			}


		} WB;

		class ClientHelper
		{
		public:
			ClientHelper()
			{
				mbInLargeMessage = false;
				mcLargeMessageBuffer = 0;
				mnLargeMessageSize = 0;
				mnRefs = 2;
			}

			int mnRefs;
			int mnLargeMessageType;
			bool mbInLargeMessage;
			char *mcLargeMessageBuffer;
			int mnLargeMessageSize;

			~ClientHelper()
			{
				--mnRefs;
				if( mnRefs <= 0 )
				{
					if( mcLargeMessageBuffer )
						delete [] mcLargeMessageBuffer;
					mcLargeMessageBuffer = 0;
				}
			}




		};

		class NETPROCCLIENT: public NetworkMessageProcFunctor
		{
		public:

			NETPROCCLIENT()
			{
				mbInLargeMessage = false;
				mnLargeMessageType = 0;
				mcLargeMessageBuffer = 0;
				mnLargeMessageSize = 0;
			}

			bool mbInLargeMessage;
			int mnLargeMessageType;
			char *mcLargeMessageBuffer;
			int mnLargeMessageSize;
			

			void processMessage( int nMessage, char *pBuffer, int nSize, int nUser );
			void operator () ( NetworkMessage msg );

		} netProcClient;

		class NETPROCSERVER: public NetworkMessageProcFunctor
		{
		public:
			std::vector<ClientHelper> clients;
			void processMessage( int nMessage, char *pBuffer, int nSize, int nUser );
			void operator () ( NetworkMessage msg );
		} netProcServer;

		class TESTCONSOLEFUNCT: public ConsoleFunctor
		{
		public:
			void operator () (char *str)
			{
				ModularEngine::TestModule::getSingleton().consoleText->addText( str );
			}
		} TestConsole;

	// Console Button functor
		class SENDCONSOLECMD: public Functor
		{
			void operator() ();
		} sendConsoleCmd;	

		class HOSTSERVER: public Functor, public Singleton<HOSTSERVER>
		{
		public:
			HUDWindow *mHostServerWindow;

			HUDCheckbox *isAdmin;
			HUDLabel *isAdminLabel;
			HUDLabel *adminUserNameLabel;

			HUDButton *hostServer;
			HUDButton *cancelButton;

			HUDTextbox *adminUsername;

			HOSTSERVER()
			{
				mHostServerWindow = 0;
			}

			class startHosting: public Functor, public Singleton<startHosting>
			{
			public:
				void operator() ();
			};

			class cancel: public Functor, public Singleton<cancel>
			{
			public:
				void operator () ()
				{
					HOSTSERVER::getSingleton().mHostServerWindow->mbVisible = false;
					TestModule::getSingleton().mMainMenu->mbVisible = true;
				}
			};

			void operator() ();
		};

		class CONNECTTOSERVER: public Functor, public Singleton<CONNECTTOSERVER>
		{
		public:
			HUDWindow *connectWindow;

			HUDTextbox *ipText;
			HUDLabel *ipLabel;

			HUDButton *cancelButton;
			HUDButton *connectButton;

			class connectToHost: public Functor, public Singleton<connectToHost>
			{
			public:
				void operator () ()
				{
					ClientModule::getSingleton().ipAddress = CONNECTTOSERVER::getSingleton().ipText->getText();
					if( !ClientModule::getSingleton().initClient() )
					{
						TestModule::getSingleton().mConsoleWindow->mbVisible = true;
					} else 
					{
						CONNECTTOSERVER::getSingleton().connectWindow->mbVisible = false;
						ClientModule::getSingleton().sendMessage( GM_NEWUSER );
					}
				}
			};

			class cancel: public Functor, public Singleton<cancel>
			{
			public:
				void operator () ()
				{
					CONNECTTOSERVER::getSingleton().connectWindow->mbVisible = false;
					TestModule::getSingleton().mMainMenu->mbVisible = true;
				}
			};

			CONNECTTOSERVER()
			{
				connectWindow = 0;
			}

			void operator() ();
		};

		class SHOWSETTINGS: public Functor
		{
			void operator () ()
			{
				TestModule::getSingleton().mMainMenu->mbVisible = false;
			}
		} showSettingsF;

		class EXIT: public Functor
		{
			void operator() ()
			{
				EngineCore::getSingleton().stop();
			}
		} exitF;

		class KeyboardSend: public KMFunctor
		{
			void operator() (UINT nKey )
			{
				switch( (unsigned char)nKey )
				{
				case 9:
					// F1 is pressed
					EngineCore::getSingleton().stop();
					break;
				// if escape is pressed toggle main menu
				case 6:
					TestModule::getSingleton().mMainMenu->mbVisible = !TestModule::getSingleton().mMainMenu->mbVisible;
					break;
				case 96:
					// '`' key

					// Toggle the console
					TestModule::getSingleton().mConsoleWindow->mbVisible = !TestModule::getSingleton().mConsoleWindow->mbVisible;
					break;
				default:
					ModularEngine::HUDModule::getSingleton().keyboardInput( nKey );
					break;
				}
			}

		} keyboardSend;

		class MouseSend: public KMFunctor
		{
			void operator() (UINT nKey )
			{
				Vector2d mouse = WindowModule::getSingleton().getMouse();
				HUDModule::getSingleton().mouseInput( mouse.x, mouse.y, nKey );
			}

		} mouseSend;

		void initWindows();
		int initialize();
		int run();
	};
}


#endif